跳到主要内容

Kubernetes 配置管理 ConfigMap

在 Kubernetes 中,一般有 ConfigMap 和 Secret 两种对象,可以用来做配置管理。

创建 ConfigMap

ConfigMap 通过键值对来存储信息,是个 namespace 级别的资源。在 kubectl 使用时,我们可以简写成 cm。

定义一个 CM 来测试 cm-demo-mix.yaml

apiVersion: v1
kind: ConfigMap
metadata:
name: cm-demo-mix # 对象名字
namespace: demo # 所在的命名空间
data: # 这是跟其他对象不太一样的地方,其他对象这里都是 spec
# 每一个键都映射到一个简单的值
player_initial_lives: "3" # 注意这里的值如果数字的话,必须用字符串来表示
ui_properties_file_name: "user-interface.properties"
# 也可以来保存多行的文本
game.properties: |
enemy.types=aliens,monsters
player.maximum-lives=5
user-interface.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true

再定义一个 cm-demo-all-env.yaml

apiVersion: v1
kind: ConfigMap
metadata:
name: cm-demo-all-env
namespace: demo
data:
SPECIAL_LEVEL: very
SPECIAL_TYPE: charm

现在我们来创建这两个 ConfigMap:

$ kubectl create -f cm-demo-mix.yaml
$ kubectl create -f cm-demo-all-env.yaml
# 取得 ConfigMap 的信息
$ kubectl get cm -n demo

和 Pod 结合起来使用

Pod 必须和 ConfigMap 在同一个 namespace 下面,在创建 Pod 之前,请务必保证 ConfigMap 已经存在,否则 Pod 创建会报错。

创建一个 cm-demo-pod.yaml 文件

apiVersion: v1
kind: Pod
metadata:
name: cm-demo-pod
namespace: demo
spec:
containers:
- name: demo
image: busybox:1.28
command:
- 'bin/sh'
- '-c'
- 'echo PLAYER_INITIAL_LIVES=$PLAYER_INITIAL_LIVES && sleep 10000'
# 定义环境变量
env:
- name: PLAYER_INITIAL_LIVES # 请注意这里和 ConfigMap 中的键名是不一样的
valueFrom:
configMapKeyRef:
name: cm-demo-mix # 这个值来自 ConfigMap
key: player_initial_lives # 需要取值的键
- name: UI_PROPERTIES_FILE_NAME
valueFrom:
configMapKeyRef:
name: cm-demo-mix
key: ui_properties_file_name
# 可以将 configmap 中的所有键值对都通过环境变量注入容器中
envFrom:
- configMapRef:
name: cm-demo-all-env
# 可以将 configmap 中的某个键值对注入到文件中
volumeMounts:
- name: full-config # 这里是下面定义的 volume 名字
mountPath: '/config' # 挂载的目标路径
readOnly: true
- name: part-config
mountPath: /etc/game/
readOnly: true
volumes: # 可以在 Pod 级别设置卷,然后将其挂载到 Pod 内的容器中
- name: full-config # 这是 volume 的名字
configMap:
name: cm-demo-mix # 提供你想要挂载的 ConfigMap 的名字
- name: part-config
configMap:
name: cm-demo-mix
items: # 也可以只挂载部分的配置
- key: game.properties
path: properties

在上面的这个例子中,几乎囊括了 ConfigMap 的几大使用场景:

  • 命令行参数;
  • 环境变量,可以只注入部分变量,也可以全部注入;
  • 挂载文件,可以是单个文件,也可以是所有键值对,用每个键值作为文件名。
$ kubectl create -f cm-demo-pod.yaml

创建成功后,我们 exec 到容器中看看:

$ kubectl exec -it cm-demo-pod -n demo sh

可以看到注入进去的两个环境变量

$ ls -al /config/

可以看到挂载进去的文件,里面也包含了对应的配置文件

在上面 ls -alh /config/ 后,我们看到挂载的文件中存在软链接,都指向了 ..data 目录下的文件。这样做的好处,是 kubelet 会定期同步检查已经挂载的 ConfigMap 是否是最新的,如果更新了,就是创建一个新的文件夹存放最新的内容,并同步修改 ..data 指向的软链接。

Secret 敏感数据

一般我们只把一些非敏感的数据保存到 ConfigMap 中,敏感的数据就要保存到 Secret 中了。

提示

其实目前 Secret 的实现,就是 ConfigMap 把 value 用 base64 encode了一下,所以,其实不存在任何安全性,只要 decode 一下就能出现原来结果,相当于明文存储。

base64 这玩意儿都不能叫做加密,只能叫做编码,所以都不说 encrypt,而是 encode 和 decode。

当然,Kubernetes 社区有在计划对 Secret 进行下一步的安全性增强,当然这是后话了,截止目前为止,Secret 基本和 ConfigMap一样是明文存储。

可以用 Secret 来保存一些敏感的数据信息,比如密码、密钥、token 等。在使用的时候, 跟 ConfigMap 的用法基本保持一致,都可以用来作为环境变量或者文件挂载。

Kubernetes 自身也有一些内置的 Secret,主要用来保存访问 APIServer 的 service account token

通过命令行 help 可以看到 kubectl 能够创建多种类型的 Secret。

$ kubectl create secret  -h

私有容器仓库的身份信息

先来创建一个 Secret 来保存访问私有容器仓库的身份信息

$ kubectl create secret -n demo docker-registry regcred \
--docker-server=yourprivateregistry.com \
--docker-username=allen \
--docker-password=mypassw0rd \
--docker-email=allen@example.com

再检查一下是否创建成功

$ kubectl get secret -n demo regcred

这里我们可以看到,创建出来的 Secret 类型是 kubernetes.io/dockerconfigjson

$ kubectl describe secret -n demo regcred

为了防止 Secret 中的内容被泄漏,kubectl get 和 kubectl describe 会避免直接显示密码的内容。但是我们可以通过拿到完整的 Secret 对象来进一步查看其数据:

$ kubectl get secret -n demo regcred -o yaml

这里我们发现 .dockerconfigjson 是一段乱码,我们用 base64 解压试试看:

$ kubectl get secret regcred -n demo --output="jsonpath={.data.\.dockerconfigjson}" | base64 --decode

这实际上跟我们通过 docker login 后的 ~/.docker/config.json 中的内容一样。 至此,我们发现 Secret 和 ConfigMap 在数据保存上的最大不同。Secret 保存的数据都是通过 base64 加密后的数据。

普通的密码加密

平时使用较为广泛的还有另外一种 Opaque 类型的 Secret:

创建文件 secret-demo.yaml

apiVersion: v1
kind: Secret
metadata:
name: dev-db-secret
namespace: demo
type: Opaque
data: # 这里的值都是 base64 加密后的
password: UyFCXCpkJHpEc2I9
username: ZGV2dXNlcg==
$ kubectl create -f secret-demo.yaml
$ kubectl get secret -n demo dev-db-secret

或者我们也可以通过如下等价的 kubectl 命令来创建出来:

$ kubectl create secret generic dev-db-secret -n demo \
--from-literal=username=devuser \
--from-literal=password='S!B\*d$zDsb='

或通过文件来创建对象,比如:

$ echo -n 'username=devuser' > ./db_secret.txt
$ echo -n 'password=S!B\*d$zDsb=' >> ./db_secret.txt
$ kubectl create secret generic dev-db-secret -n demo \
--from-file=./db_secret.txt

有时候为了方便,也可以使用stringData,这样可以避免自己事先手动用 base64 进行加密。

apiVersion: v1
kind: Secret
metadata:
name: dev-db-secret
namespace: demo
type: Opaque
stringData:
password: devuser
username: S!B\*d$zDsb=

在 Pod 中使用 Secret:

# pod-secret.yaml
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
namespace: demo
spec:
containers:
- name: demo-container
image: busybox:1.28
command: [ "/bin/sh", "-c", "env" ]
envFrom:
- secretRef:
name: dev-db-secret
restartPolicy: Never

创建 Pod

$ kubectl create -f pod-secret.yaml

创建成功后,我们来查看下:

# 最后加的这个 -w 是为了持续查看
$ kubectl get pod -n demo secret-test-pod
$ kubectl logs -f -n demo secret-test-pod

我们可以在日志中看到命令 env 的输出,看到环境变量 username 和 password 已经正确注入。

References